package org.uc3m.pfc.marker.manager;

import java.util.List;

import org.opencv.android.CameraBridgeViewBase;
import org.opencv.core.Mat;
import org.opencv.core.MatOfPoint2f;
import org.opencv.core.Point;
import org.opencv.imgproc.Imgproc;
import org.uc3m.pfc.lifecycle.LifeCycle;
import org.uc3m.pfc.lifecycle.State;

import static org.uc3m.pfc.lifecycle.State.*;
import org.uc3m.pfc.marker.detection.Marker;

import android.util.Log;

/**
 * 
 * @author esteban
 *
 */
public final class MarkerFilter extends LifeCycle implements IMarkerFilter {

	/** Milliseconds in one second. */
	private static final long MILLIS_IN_SECOND = 1000;
	
	/** Radius variable, for performance needs final modifier. */
	private final float[] radius = new float[1];
	/** Radius variable, for performance needs final modifier. */
	private final Point center = new Point();
	/** Radius variable, for performance needs final modifier. */
	private final MatOfPoint2f mat = new MatOfPoint2f();	
	
	private static IMarkerFilter instance;
	
	/** Last marker id detected. */
	private int id;
	/** First time detected in milliseconds. */
	private long start;
	/** Last time detected in milliseconds. */
	private long end;
	
	/** Difference from end-start in seconds. */
	private long time;
	
	/** Constant for enable commands. */
	private static final long MAX_TIME_DEFAULT = 5;
	
	private long maxTime;
	
	private double currentCenterX;

	
	/**
	 * Default constructor.
	 */
	private MarkerFilter() {
		
		this.setState(detecting);
		this.setSubstate(noTracking);

		setMaxTime(MAX_TIME_DEFAULT);
	}

	public static IMarkerFilter getInstance() {
		
		if (instance == null) {
			instance = new MarkerFilter();
		}
		
		return instance;
	}

	@Override
	public Marker filterMultiplesMarkers(final List<Marker> markers, 
			final Mat frame) {
		
		// if only one marker, return this always
		if (markers.size() == 1) {
			
			currentCenterX = 0;
			return markers.get(0);
		}

		// if state processing return always selected id
		if (this.getState() == processing) {

			currentCenterX = 0;
			
			for (Marker marker : markers) {
				if (marker.id == id) {
					return marker;
				}
			}
		}
		
		// state detecting with more than one marker
		
		double distance = Long.MAX_VALUE;
		int indexSelected = 0;

		double frameCenterX = 0;
		if (currentCenterX == 0) {
			frameCenterX = frame.width() / 2;
		} else {
			frameCenterX = currentCenterX;
		}
		
		double frameCenterY = frame.height() / 2;

		
		Log.i("Filter", "init filtering");
		
		for (int i = 0; i < markers.size(); i++) {
			
			mat.fromList(markers.get(i).getContour().toList());
			Imgproc.minEnclosingCircle(mat, center, radius);
			
			double dx = center.x - frameCenterX;
			double dy = center.y - frameCenterY;
			
			double distanceAux = Math.sqrt((dx * dx) + (dy * dy));
			
			
			if (distanceAux < distance) {
				distance = distanceAux;
				indexSelected = i;
				currentCenterX = center.x;
			}
		}
		
		
		return markers.get(indexSelected);
	}	
	
	@Override
	public void enableByTime(final int currentId) {
		
		if (id != currentId) {
			
			this.setSubstate(noTracking);

		}

		id = currentId;

		if (start == 0) {
			start = System.currentTimeMillis();
			Log.i("Marker", "Marker id candidato: " + currentId);
			
			this.setSubstate(trackingOnGoing);

		} else {
			end = System.currentTimeMillis();

			time = (end - start) / MILLIS_IN_SECOND;

			if (time >= maxTime) {

				this.setSubstate(trackingFinished);
			}

		}
		 

	}

	@Override
	public void resetTimer() {

		id = 0;
		start = 0;
		end = 0;
		time = 0;

		currentCenterX = 0;
		
		this.setSubstate(noTracking);
		CameraBridgeViewBase.enableCandidateMarker = false;
	}

	@Override
	public long getTime() {
		return maxTime - time;
	}

	@Override
	public void setId(final int pid) {

		this.id = pid;
	}
	
	@Override
	public void setState(final State pstate) {
		super.setState(pstate);
		
		switch (pstate) {
		case detecting:
			
			resetTimer();
			
			break;
		case processing:
			
			break;

		default:
			break;
		}
	}
	
	@Override
	public void setSubstate(final State psubstate) {
		super.setSubstate(psubstate);
		
		switch (psubstate) {
		case noTracking:
			
			id = 0;
			start = 0;
			end = 0;
			time = 0;

			currentCenterX = 0;

			CameraBridgeViewBase.enableCandidateMarker = false;
			
			break;
		case trackingOnGoing:
			
			CameraBridgeViewBase.enableCandidateMarker = true;
			
			break;
		case trackingFinished:
			
			start = 0;
			end = 0;
			id = 0;

			CameraBridgeViewBase.enableCandidateMarker = false;
			
			break;			
		default:
			break;
		}
		
	}
	
	@Override
	public long getMaxTime() {
		return maxTime;
	}
	
	/**
	 * Set the maximum time to enable comands.
	 * 
	 * @param pmaxTime to set
	 */
	public void setMaxTime(final long pmaxTime) {
		this.maxTime = pmaxTime;
		this.resetTimer();
	}
	
}
